#pragma once
#include "DGeometry.h"
#include "../DOde.h"

namespace Skill
{
	namespace Ode
	{
		ref class DMatrix4;
		ref class DVector3;
		ref class DSpace;

		namespace Geometries
		{
			ref class DTriMeshData;
			ref class DTriMesh;
			ref class DRay;

			public delegate int DTriCallback(DTriMesh^ triMesh, DGeometry^ refObject, int triangleIndex);
			public delegate void DTriArrayCallback(DTriMesh^ triMesh, DGeometry^ refObject, array<int>^ triIndices);
			public delegate int DTriRayCallback(DTriMesh^ triMesh, DRay^ ray, int triangleIndex, dReal u, dReal v);

			public ref class DTriMesh : DGeometry
			{
			private:
				DTriCallback^ callback;
				DTriArrayCallback^ arrayCallback;
				DTriRayCallback^ rayCallback;
			protected:
				virtual void CollectManagedMemory() override;			
			public:

				///<summary>
				/// Per triangle callback. Allows the user to say if he wants a collision with
				/// a particular triangle.
				///</summary>							
				property DTriCallback^ Callback
				{
					DTriCallback^ get();
					void set(DTriCallback^ value);
				}

				///<summary>
				/// Per object callback. Allows the user to get the list of triangles in 1
				/// shot. Maybe we should remove this one.
				///</summary>				
				property DTriArrayCallback^ ArrayCallback
				{
					DTriArrayCallback^ get();
					void set(DTriArrayCallback^ value);
				}

				///<summary>
				/// Ray callback.
				/// Allows the user to say if a ray collides with a triangle on barycentric
				/// coords. The user can for example sample a texture with alpha transparency
				/// to determine if a collision should occur.
				///</summary>				
				property DTriRayCallback^ RayCallback
				{
					DTriRayCallback^ get();
					void set(DTriRayCallback^ value);
				}

				///<summary>
				/// We need to set the last transform after each time step for 
				/// accurate collision response. These functions get and set that transform.
				/// It is stored per geom instance, rather than per dTriMeshDataID.
				///</summary>
				REF_PROP_DECLARATION_GETSET(DMatrix4,LastTransform);				


				///<summary>				
				/// Construction.
				///</summary>
				DTriMesh(DSpace^ space, DTriMeshData^ data);

				REF_PROP_DECLARATION_GETSET(DTriMeshData,Data)

				///*
				//* Per triangle callback. Allows the user to say if he wants a collision with
				//* a particular triangle.
				//*/
				//typedef int dTriCallback(dGeomID TriMesh, dGeomID RefObject, int TriangleIndex);
				//ODE_API void dGeomTriMeshSetCallback(dGeomID g, dTriCallback* Callback);
				//ODE_API dTriCallback* dGeomTriMeshGetCallback(dGeomID g);

				///*
				//* Per object callback. Allows the user to get the list of triangles in 1
				//* shot. Maybe we should remove this one.
				//*/
				//typedef void dTriArrayCallback(dGeomID TriMesh, dGeomID RefObject, const int* TriIndices, int TriCount);
				//ODE_API void dGeomTriMeshSetArrayCallback(dGeomID g, dTriArrayCallback* ArrayCallback);
				//ODE_API dTriArrayCallback* dGeomTriMeshGetArrayCallback(dGeomID g);

				///*
				//* Ray callback.
				//* Allows the user to say if a ray collides with a triangle on barycentric
				//* coords. The user can for example sample a texture with alpha transparency
				//* to determine if a collision should occur.
				//*/
				//typedef int dTriRayCallback(dGeomID TriMesh, dGeomID Ray, int TriangleIndex, dReal u, dReal v);
				//ODE_API void dGeomTriMeshSetRayCallback(dGeomID g, dTriRayCallback* Callback);
				//ODE_API dTriRayCallback* dGeomTriMeshGetRayCallback(dGeomID g);								


				//// enable/disable/check temporal coherence
				void EnableTC(int geomClass, bool enable);
				bool IsTCEnabled(int geomClass);

				void EnableTC(OdeClassses geomClass, bool enable);
				bool IsTCEnabled(OdeClassses geomClass);

				///*
				//* Clears the internal temporal coherence caches. When a geom has its
				//* collision checked with a trimesh once, data is stored inside the trimesh.
				//* With large worlds with lots of seperate objects this list could get huge.
				//* We should be able to do this automagically.
				//*/
				void ClearTCCache();
				
				///<summary>
				/// Gets a triangle.
				///</summary>
				void GetTriangle(int index, [OutAttribute]DVector3^ %v0, [OutAttribute]DVector3^ %v1, [OutAttribute]DVector3^ %v2);

				///<summary>
				/// Gets the point on the requested triangle and the given barycentric
				/// coordinates.
				///</summary>
				DVector3^ GetPoint(int Index, dReal u, dReal v);

				VALUE_PROP_DECLARATION_GET(int,TriangleCount);
			};
		}
	}
}